#include <stdio.h>

// fopen: 如果打开成功,就会返回文件信息区的地址 打开文件失败,就会返回NULL
// 'w': 以写的形式打开文件,如果找不到就会创建一个新的文件

// 一般来说,文件指针应该使用名称: 'pf',但由于这里一次性将函数全写好了,所以使用不同的变量名做区分

void test_1 ()
{

    int a = 1000 ;
    
    FILE* pa = (FILE*) fopen ("test1.txt" , "wb") ; // write binary , 二进制的形式写入文件中

    fwrite (&a , 4 , 1 , pa) ; // 以二进制的形式写入到数据里面

    fclose (pa) ;

    pa = NULL ; // 当关闭文件的时候,此时的内存缓冲区会被删除,所以会变为野指针,所以要置空

}

void test_2 ()
{

    // 使用绝对路径去打开文件

    FILE* pb = fopen ("F:\\codes\\gitee location\\c-lesson\\homework\\test2.txt" , "w") ; // 这里要注意,在初学c语言的时候就说过了转义字符的概念
    //为了防止反斜杠被转义然后使用出错,所以这里我们要再加多一个反斜杠来转义反斜杠的内容(呃)

    // 使用相对路径来打开文件
    // . -- 当前路径
    // .. -- 上一路径

    FILE* pc = fopen (".\\test3.txt" , 'w') ;

    FILE* pd = fopen ("..\\the 23rd homework\\test file\\test4.txt" , 'w') ; // 这里是先在上一级的路径再进到本目录,再进到test file目录里面查找文件

    // 这里展示需要在使用fopen函数之后要写判断函数来判断是否打开成功(只写一个指针),利用fopen打开失败的机制

    if (pb == NULL) // 这里当fopen打开失败的时候,指针只会接收到空指针
    {

        perror ("fopen") ; // 这里的perror,其实就是相当于strerror(errno) + printf的结合,更方便

        return 1 ; // 返回一个不为0的值,表明程序出现错误

    }

    else
    {

        printf ("打开文件成功") ; // 打印表示打开文件成功

    }

    // 以上操作全为用读的方式打开文件

    // 当打开完文件,就一定要关闭文件 fopen和fclose是成对出现的要

    fclose (pb) ;

    fclose (pc) ;

    fclose (pd) ;

    pb = NULL , pc = NULL , pd = NULL ; // 同时文件要置空

}

// 写字符到文件当中
// 对一个文件操作的标准流程

void test_3 ()
{

    FILE* pe = fopen ("data.txt" , 'w') ;

    if (pe == NULL)
    {

        perror ("pe") ;

        exit (1) ;

    }

    // 写文件

    fputc ('a' , pe) ; // 这里要额外提醒一下,所有文件操作的函数和其模式都有针对不同情况的应对情况,所以说这些需要自己去记忆了,如果使用错误会有问题

    fputc ('b' , pe) ;

    fputc ('c' , pe) ;

    fputc ('d' , pe) ;

    fputc ('e' , pe) ;

    fputc ('f' , pe) ;

    for (int i = 'a' ; i <= 'z' ; i++) // 这里展示利用循环的方式来写文件,两种都可以学习
    {

        fputc (i , pe) ; // 这里的区别在于,因为i已经存储了字符的内容,所以不需要用单引号括起来了

    }

    // 关闭文件两件套

    fclose (pe) ;

    pe = NULL ;

}

// 从文件中读取字符(承接上一部分函数的内容)

void test_4 ()
{

    FILE* pf = fopen ("data.txt" , "r") ;

    if (pf = NULL)
    {

        perror ("fopen") ;

        return 1 ;

    }

    // 读文件

    int ch = fgetc (pf) ;

    printf ("%c" , ch) ; // 此时读取出的是a,其实是a的ASCII码

    ch = fgetc (pf) ;

    printf ("%c" , ch) ; // b

    ch = fgetc (pf) ;

    printf ("%c" , ch) ; // c

    ch = 0 ;

    while ((ch = getc (pf) != EOF)) // 设立循环来读取文件内容,而且这里循环条件还利用了一个机制: getc读取到文件末尾或读取失败会返回EOF值
    {

        printf ("%c" , ch) ; // 读取完后直接打印

    }

    // 关闭文件

    fclose (pf) ;

    pf = NULL ;

}

void test_5 ()
{

    FILE* pg = fopen ("data.txt" , "w") ;

    if (pg == NULL)
    {

        perror ("fopen") ;

        return 1 ;

    }

    // 写文件

    fputs ("hello world\n" , pg) ;

    fputs ("I'm biter\n" , pg) ;

    // 关闭文件
    
    fclose (pg) ;

    pg = NULL ;

}

// 从文件读取一行数据

void test_6 ()
{

    FILE* ph = fopen ("data.txt" , "r") ;

    if (ph == NULL)
    {

        perror ("fopen") ;

        return 1 ;

    }

    // 读文件

    char arr [20] ;

    fgets (arr , 20 , ph) ; // fgets函数,用于获取文本里面文本行,读取二十个字节之后就会结束读取并放在arr数组里面

    // 关闭文件

    fclose (ph) ;

    ph = NULL ;

}

// 向文件中写数据

void test_7 ()
{

    FILE* pi = fopen ("data.txt" , "w") ;

    if (pi == NULL)
    {
        
        perror ("fopen") ;

        return 1 ;

    }

    // 写文件

    char arr [20] = "hello bit" ;

    int num = 100 ;

    double pai = 3.14 ;

    fprintf (pf , "%s %d  %.2lf" , arr , num , pai) ; // 使用fprintf函数格式化写入文件里面

    // 关闭文件

    fclose (fi) ;

    fi = NULL ;

}

// 下面开始是针对结构体的操作

struct S
{

    char arr[20] ;

    int num ;

    double pai ;

} ;


void test_8 ()
{

    FILE* pj = fopen ("data.txt" , "w") ;

    if (pj == NULL)
    {

        perror ("fopen") ;

        return 1 ;

    }

    struct S s1 = {"hello" , 200 , 3.14} ;

    // 写文件

    fprintf (pj , "%s %d %.2lf" , s1.arr , s1.num , s1.pai) ;

    // 关闭文件

    fclose (pj) ;

    pj == NULL ;

}

void test_9 ()
{

    struct S s2 = {"hello" , 202 , 3.14} ;

    fprintf (stdout , "%s %d %.2f" , s2.arr , s2.num , s2.pai) ; // 这是打印标准输出流的语句

}

void test_10 ()
{

    FILE* pl = fopen ("data.txt" , "r") ;

    if (pl == NULL)
    {

        perror ("fopne") ;

        return 1 ;

    }

    struct S s3 = { 0 } ;

    // 读文件

    fscanf (pl , "%s %d %lf" , s.arr , &(s.num) , &(s.pai)) ; // 这里要注意,由于我们要输入数值去存入数据,所以这里要用取地址,但arr就不用

    printf ("%s %d %lf\n" , s3.arr , s3.num , s3.pai) ;

    // 关闭文件

    fclose (pl) ;

    pl = NULL ;

}

void test_11 ()
{

    struct S s4 = { "world" , 202 , 3.14 } ;

    char arr [30] = { 0 } ;

    sprintf (arr , "%s %d %lf" , s4.arr , s4.num , s4.pai) ;

    printf ("字符串: %s\n" , arr) ;

    // 从arr中提取出的数据

    struct S t = { 0 } ;

    sscanf (arr , "%s %d %lf" , t.arr , &(t.num) , &(t.pai)) ; // 将arr的内容写入到结构体变量t里面

    printf ("结构体: %s %d %lf\n" , t.arr , t.num , t.pai) ;

}

void test_12 ()
{

    FILE* pm = fopen ("bin.txt" , "wb") ;

    if (pm == NULL)
    {

        perror ("fopen") ;

        return 1 ;

    }

    // 二进制的形式写文件

    int arr [] = { 1 , 2 , 3 , 4 , 4 } ;

    fwrite (arr , sizeof (arr [0]) , 5 , pm) ; // fwrite函数,将数组arr的内容写入到pm指向的文件里面 另外,由于pm所打开的文件是以二进制的文件写入的,所以存储的都是二进制转换后的值

    // 关闭文件

    fclose (pm) ;

    pm = NULL ;
    
}

void test_13 ()
{

    FILE* pn = fopne ("bin.txt" , "rb") ;

    if (pn == NULL)
    {

        perror ("fopen") ;

        return 1 ;

    }

    // 二进制的形式读文件

    int arr [10] = { 0 } ;

    fread (arr , sizeof (int) , 5 , pn) ;

    int i = 0 ;

    for (i = 0 ; i < 5 ; i++)
    {

        printf ("%d " , arr [i]) ;

    }

    fclose (pn) ;

    pn = NULL ;

}

// 这一组文件操作函数是用于查找文件内某一值并返回其下标的

void test_14 ()
{

    FILE* po = fopen ("data.txt" , "r") ;

    if (po == NULL)
    {

        perror ("fopen") ;

        return 1 ;

    }

    int ch = getc (po) ;

    fputc (ch , stdout) ;

    // 定位文件指针指向 'd'

    fseek (po , -3 , SEEK_END) ;

    ch = fgetc (po) ;

    fputc (ch , stdout) ;

    // 光标指向的是 'e'

    long int r = ftell (po) ;

    pirntf ("%ld \n" , r) ; // 4

    rewind (po) ; // 让文件指针回到起始位置

    ch = fgetc (po) ;

    fputc (ch , stdout) ; // a

    fclose (po) ;
    
    po = NULL ;

}

int main ()
{

    test_1 () ;

    test_2 () ;

    test_3 () ;

    test_4 () ;

    test_5 () ;

    test_6 () ;

    test_7 () ;

    test_8 () ;

    test_9 () ;

    test_10 () ;

    test_11 () ;

    test_12 () ;

    test_13 () ;

    test_14 () ;

    return 0 ;

}